#include "./../inc/render.h"
#include "./../inc/log.h"
#include "./../inc/system.h"

#if defined(_WIN32)

#include <windows.h>
 
#ifdef DX_RENDER

#pragma comment(lib, "./../../ufo_external/dx9/lib/d3d9.lib")
#pragma comment(lib, "./../../ufo_external/dx9/lib/d3dx9.lib")

#include "d3dx9.h"
LPDIRECT3D9             g_pD3D;
LPDIRECT3DDEVICE9       g_pd3dDevice;
LPDIRECT3DVERTEXBUFFER9 g_pVB;
LPDIRECT3DINDEXBUFFER9  g_pIB;

extern HWND					   g_hWnd;

LPDIRECT3DSURFACE9 g_backbuffer, g_systemBuffer, g_backDepthBuffer;

typedef struct _CUSTOMVERTEX
{
	D3DXVECTOR3 position; // The position
	D3DCOLOR    color;    // The color
	FLOAT       tu, tv;   // The texture coordinates
}CUSTOMVERTEX;

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

#else//### opengl

//#pragma comment(lib, "./../../ufo_external/opengl/lib/glut32.lib")
#pragma comment(lib, "./../../ufo_external/opengl/lib/glew32.lib")
#pragma comment(lib, "OPENGL32.LIB")

#include "glew.h"
#include "gl.h"
#include "glu.h"		
#include "glaux.h"

HDC			g_hDC;		// Private GDI Device Context
HGLRC		g_hRC;		// Permanent Rendering Context

#endif

#if(_WIN32_WINNT < 0x0400)
WINUSERAPI BOOL WINAPI UpdateLayeredWindow(HWND hWnd,HDC hdcDst,POINT *pptDst,SIZE *psize,HDC hdcSrc,POINT *pptSrc,COLORREF crKey,BLENDFUNCTION *pblend,DWORD dwFlags);
#endif

#elif defined(__APPLE__)

#include "TargetConditionals.h"

#if TARGET_OS_IPHONE == 1
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
//#include <OpenGLES/ES2/gl.h>
//#include <OpenGLES/ES2/glext.h>

#define OGL_ES 1

#else
#include <OpenGL/glu.h>
#include <GLUT/GLUT.h>
#endif

#elif defined(ANDROID)

#include <GLES/gl.h>
#include <GLES/glext.h>

#define OGL_ES 1

#endif

#include <math.h>
#include <assert.h>
#include <string.h>
#include "stack.h"

ufoRect g_screenRect, g_freshRect;
UFO_RENDER g_render;

RENDER_CMP_FUNC g_stencilFunc;

int g_stentilTest;
int g_stentilTestRef;
int g_multiSample;
int g_depthTest;
int g_cullface;

UFO_Stack g_stack_projection, g_stack_modelview, g_stack_viewport;

HCANVAS g_defaultCanvas, g_currentCanvas;

int internal_render_calc_pot(int x)
{
	int mask = 0x10000000;
	while (mask) {
		if (mask & x) {
			if (mask != x) {
				mask <<= 1;
			}
			return mask;
		}
		mask >>= 1;
	}
	return 0;
}

void ufoAddFreshRect(const ufoRect * lpRect)
{
	ufoRect newRect = {0}; 
	if (!lpRect) {
		lpRect = &g_screenRect;
	}
	ufoRectMerge(lpRect, &g_freshRect, &newRect);
	g_freshRect = newRect;
	if (g_freshRect.left < g_screenRect.left)
		g_freshRect.left = g_screenRect.left;
	if (g_freshRect.top < g_screenRect.top)
		g_freshRect.top = g_screenRect.top;
	if (g_freshRect.right > g_screenRect.right)
		g_freshRect.right = g_screenRect.right;
	if (g_freshRect.bottom > g_screenRect.bottom)
		g_freshRect.bottom = g_screenRect.bottom;
}

void ufoClearFreshRect()
{
	ufoRect newRect = {0};
	g_freshRect = newRect;   
}

int ufoGetFreshRect(ufoRect * pRect)
{
	*pRect = g_freshRect;
	return !ufoRectIsEmpty(pRect);
}

int ufoSetPixelShader(int id)
{
#ifdef OGL_RENDER
	//glCreateShader(GL_FRAGMENT_SHADER);
#else

#endif
	return 0;
}

int ufoSetDepthTest(int enabled)
{
    if (enabled == g_depthTest) {
        return 1;
    }
#ifdef OGL_RENDER
	if (enabled)
		glEnable(GL_DEPTH_TEST);
	else 
		glDisable(GL_DEPTH_TEST);
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_ZENABLE, enabled);
	}
#endif
    g_depthTest = enabled;
	return 1;
}

int ufoSetCullFace(int enabled)
{
    /*if (enabled == g_cullface) {
        return 1;
    }*/
#ifdef OGL_RENDER
	if (enabled)
		glEnable(GL_CULL_FACE);
	else
		glDisable(GL_CULL_FACE);
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_CULLMODE, enabled ? D3DCULL_CCW : D3DCULL_NONE);
	}
#endif
    g_cullface = enabled;
	return 1;
}

int ufoSetStencilTest(int enabled)
{
    //if (enabled == g_stentilTest)
    //    return 1;

	g_stentilTest = enabled;
#ifdef OGL_RENDER
	if (enabled)
		glEnable(GL_STENCIL_TEST);
	else
		glDisable(GL_STENCIL_TEST);
	return 1;
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILENABLE, enabled);
		return 1;
	}
#endif
	return 0;
}

int ufoSetStencilFunc(RENDER_CMP_FUNC func)
{
	//if (func == g_stencilFunc) 
	//	return 1;

	g_stencilFunc = func;

#ifdef OGL_RENDER
	switch (func) {
	case RENDER_CMP_ALWAYS:
		func = GL_ALWAYS;
		break;

	case RENDER_CMP_NEVER:
		func = GL_NEVER;
		break;

	case RENDER_CMP_EQUAL:
		func = GL_EQUAL;
		break;
	}
	glStencilFunc(func, g_stentilTestRef, 0xffffffff);
	return 1;
#else
	if (g_pd3dDevice) {
		switch (func) {
		case RENDER_CMP_ALWAYS:
			func = D3DCMP_ALWAYS;
			break;

		case RENDER_CMP_NEVER:
			func = D3DCMP_NEVER;
			break;

		case RENDER_CMP_EQUAL:
			func = D3DCMP_EQUAL;
			break;
		}
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILFUNC, func);
		return 1;
	}
#endif
	return 0;
}

int ufoSetStencilOp(UFO_STENCIL_OP fail, UFO_STENCIL_OP zfail, UFO_STENCIL_OP zpass)
{
	UFO_STENCIL_OP *op[3];
	int i = 0;
	op[0] = &fail;
	op[1] = &zfail;
	op[2] = &zpass;
#ifdef OGL_RENDER
	for (i = 0; i < 3; ++i) {
		switch (*op[i]) {
		case UFO_STENCIL_REPLACE:
			*op[i] = GL_REPLACE;
			break;

		case UFO_STENCIL_KEEP:
			*op[i] = GL_KEEP;
			break;

		case UFO_STENCIL_INCR:
			*op[i] = GL_INCR;
			break;

		case UFO_STENCIL_DESR:
			*op[i] = GL_DECR;
			break;
		}		
	}
	glStencilOp(fail, zfail, zpass);
	return 1;
#else
	if (g_pd3dDevice) {
		for (i = 0; i < 3; ++i) {
			switch (*op[i]) {
			case UFO_STENCIL_REPLACE:
				*op[i] = D3DSTENCILOP_REPLACE;
				break;

			case UFO_STENCIL_KEEP:
				*op[i] = D3DSTENCILOP_KEEP;
				break;

			case UFO_STENCIL_INCR:
				*op[i] = D3DSTENCILOP_INCR;
				break;

			case UFO_STENCIL_DESR:
				*op[i] = D3DSTENCILOP_DECR;
				break;
			}		
		}
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILFAIL, fail);
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILZFAIL, zfail);
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILPASS, zpass);
		return 1;
	}
#endif
	return 0;
}

int ufoGetStencilTest()
{
	return g_stentilTest;
}

int ufoSetStencilTestRef(int ref)
{
	//if (ref == g_stentilTestRef)
	//	return 1;

	g_stentilTestRef = ref;
#ifdef OGL_RENDER
	{
		int func = g_stencilFunc;
		switch (func) {
		case RENDER_CMP_ALWAYS:
			func = GL_ALWAYS;
			break;

		case RENDER_CMP_NEVER:
			func = GL_NEVER;
			break;

		case RENDER_CMP_EQUAL:
			func = GL_EQUAL;
			break;
		}
		glStencilFunc(func, g_stentilTestRef, 0xffffffff);
	}
	return 1;
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_STENCILREF, g_stentilTestRef);
		return 1;
	}
#endif	

	return 0;
}

int ufoGetStencilTestRef()
{
	return g_stentilTestRef;
}

int ufoSetAlphaBlend(int enabled)
{
#ifdef OGL_RENDER
	if (enabled)
		glEnable(GL_BLEND);
	else
		glDisable(GL_BLEND);
#else
	IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_ALPHABLENDENABLE, enabled); 
#endif
	return 1;
}

int ufoSetAlphaBlendFactor(UFO_ALPHABLEND_FACTOR src, UFO_ALPHABLEND_FACTOR dst)
{
#ifdef OGL_RENDER
	int blendMap[20] = {0};
	blendMap[ALPHABLEND_ZERO] = GL_ZERO;
	blendMap[ALPHABLEND_ONE] = GL_ONE;
	blendMap[ALPHABLEND_SRC_ALPHA] = GL_SRC_ALPHA;
	blendMap[ALPHABLEND_DST_ALPHA] = GL_DST_ALPHA;
	blendMap[ALPHABLEND_SRC_COLOR] = GL_SRC_COLOR;
	blendMap[ALPHABLEND_DST_COLOR] = GL_DST_COLOR;
	blendMap[ALPHABLEND_ONE_MINUS_SRC_ALPHA] = GL_ONE_MINUS_SRC_ALPHA;
	blendMap[ALPHABLEND_ONE_MINUS_DST_ALPHA] = GL_ONE_MINUS_DST_ALPHA;
	blendMap[ALPHABLEND_ONE_MINUS_SRC_COLOR] = GL_ONE_MINUS_SRC_COLOR;
	blendMap[ALPHABLEND_ONE_MINUS_DST_COLOR] = GL_ONE_MINUS_DST_COLOR;
	glBlendFunc(blendMap[src], blendMap[dst]);
#else
	int blendMap[20] = {0};
	blendMap[ALPHABLEND_ZERO] = D3DBLEND_ZERO;
	blendMap[ALPHABLEND_ONE] = D3DBLEND_ONE;
	blendMap[ALPHABLEND_SRC_ALPHA] = D3DBLEND_SRCALPHA;
	blendMap[ALPHABLEND_DST_ALPHA] = D3DBLEND_DESTALPHA;
	blendMap[ALPHABLEND_SRC_COLOR] = D3DBLEND_SRCCOLOR;
	blendMap[ALPHABLEND_DST_COLOR] = D3DBLEND_DESTCOLOR;
	blendMap[ALPHABLEND_ONE_MINUS_SRC_ALPHA] = D3DBLEND_INVSRCALPHA;
	blendMap[ALPHABLEND_ONE_MINUS_DST_ALPHA] = D3DBLEND_INVDESTALPHA;
	blendMap[ALPHABLEND_ONE_MINUS_SRC_COLOR] = D3DBLEND_INVSRCCOLOR;
	blendMap[ALPHABLEND_ONE_MINUS_DST_COLOR] = D3DBLEND_INVDESTCOLOR;
	IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_SRCBLEND, blendMap[src]);       
	IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_DESTBLEND, blendMap[dst]); 
#endif
	return 1;
}

int ufoClearStencil(int stencil)
{
#ifdef OGL_RENDER
	glStencilMask(0xffffffff);
	glClearStencil(stencil);
	glClear(GL_STENCIL_BUFFER_BIT);
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_STENCIL, 0, 1.0f, stencil);
	}
#endif
	return 1;
}

int ufoClearDepth(double depth)
{
#ifdef OGL_RENDER
	glDepthMask(1);
#if TARGET_OS_IPHONE == 1
	glClearDepthf(depth);
#elif defined(ANDROID)
	glClearDepthf(depth);
#else
	glClearDepth(depth);
#endif
	glClear(GL_DEPTH_BUFFER_BIT);

#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0-depth, 0);
	}
#endif
	return 1;
}

int ufoClearColor(float alpha, float red, float green, float blue)
{
#ifdef OGL_RENDER
	glColorMask(1, 1, 1, 1);
	glClearColor(red, green, blue, alpha);
	glClear(GL_COLOR_BUFFER_BIT);
	
	/*glDisable(GL_SCISSOR_TEST);
	glColorMask(1, 1, 1, 1);
	glDepthMask(1);
	glStencilMask(0xffffffff);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);*/
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_COLORVALUE(red, green, blue, alpha), 0.0, 0);
	}
#endif
	return 1;
}

int ufoSetMultiSample(int enabled)
{
	if (enabled == g_multiSample)
		return 1;

	g_multiSample = enabled;
#ifdef OGL_RENDER
	if (enabled)
		glEnable(0x809D); //###GL_MULTISAMPLE
	else
		glDisable(0x809D);
	return 1;
#else
	if (g_pd3dDevice) {
		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_MULTISAMPLEANTIALIAS, g_multiSample);
		return 1;
	}
#endif
	return 0;
}

int ufoGetMutilSample(int enabled)
{
	return g_multiSample;
}

int ufoSetViewPort(const ufoRect *rect)
{
	if (!rect)
		return 0;

#ifdef OGL_RENDER
	{
		int canvas_height = ufoCanvasGetCurrent()->texture->height;
		GLint x = rect->left*ufoGetDPI();
		GLint y = (canvas_height/ufoGetDPI()-rect->bottom)*ufoGetDPI();
		GLsizei width = (rect->right-rect->left)*ufoGetDPI();
		GLsizei height = (rect->bottom-rect->top)*ufoGetDPI();
		glViewport(x, y, width, height);
	}

#else
	if (g_pd3dDevice) {
		D3DVIEWPORT9 vp = {0};
		//rect = &g_screenRect;
		vp.X = (DWORD)(rect->left*ufoGetDPI());
		vp.Y = (DWORD)(rect->top*ufoGetDPI());
		vp.Width = (DWORD)((rect->right-rect->left)*ufoGetDPI());
		vp.Height = (DWORD)((rect->bottom-rect->top)*ufoGetDPI());
		vp.MinZ = 0.0f;
		vp.MaxZ = 1.0f;
		IDirect3DDevice9_SetViewport(g_pd3dDevice, &vp);
	}
#endif

	{
		ufoRect* rectStack = (ufoRect*)ufoStackTop(g_stack_viewport);
		*rectStack = *rect;
	}
	
	return 1;
}

int ufoPushViewPort()
{
	if (g_stack_viewport) {
		ufoStackPush(g_stack_viewport, ufoStackTop(g_stack_viewport));
		return 1;
	}
	return 0;
}

int ufoPopViewPort()
{
	if (g_stack_viewport) {
		int nSize = ufoStackGetSize(g_stack_viewport);
		if (nSize < 2) {
			assert(0);
			return 0;
		}

		ufoStackPop(g_stack_viewport);
		ufoSetViewPort((const ufoRect*)ufoStackTop(g_stack_viewport));
		return 1;
	}
	return 0;
}

int ufoGetViewPort(ufoRect *rect)
{
	if (g_stack_viewport && rect) {
		*rect = *(ufoRect*)ufoStackTop(g_stack_viewport);
		return 1;
	}
	return 0;
}

int ufoGetScreenRect(ufoRect * pRect)
{
	*pRect = g_screenRect;
	return 0;
}

int ufoSetScreenRect(ufoRect * pRect)
{
	g_screenRect = *pRect;
	return 0;
}

HTEXTURE ufoGenTextures(const unsigned char *texData, int width, int height, int pixelSize)
{
	HTEXTURE hTex = (HTEXTURE)malloc(sizeof(tagTEXTURE));
	memset(hTex, 0, sizeof(tagTEXTURE));
	hTex->width = width;
	hTex->height = height;
	hTex->width_POT = ufoSupportNPOT() ? width : internal_render_calc_pot(width);
	hTex->height_POT = ufoSupportNPOT() ? height : internal_render_calc_pot(height);

#ifdef OGL_RENDER
	{
		unsigned int texId = 0;
		unsigned char* data = 0;
		
		glActiveTexture(GL_TEXTURE0);
		glEnable(GL_TEXTURE_2D);

		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glGenTextures(1, &texId);
		glBindTexture(GL_TEXTURE_2D, texId);

		if (texData && !ufoSupportNPOT()) {
			int x = 0, y = 0;
			unsigned char * bit = 0;
			bit = data = malloc(hTex->width_POT*hTex->height_POT*pixelSize);
			memset(data, 0, hTex->width_POT*hTex->height_POT*pixelSize);
			for (y = 0; y < height; ++y) {
				unsigned char* _bit = bit;	
				for (x = 0; x < width; ++x) {
					_bit[0] = texData[0];
					_bit[1] = texData[1];
					_bit[2] = texData[2];
					_bit[3] = pixelSize == 3 ? 255 : texData[3];
					_bit += pixelSize;
					texData += pixelSize;
				}
				bit += pixelSize*hTex->width_POT;
			}
		}

		glTexImage2D(GL_TEXTURE_2D, 0, pixelSize == 3 ? GL_RGB : GL_RGBA, hTex->width_POT, hTex->height_POT, 
			0, pixelSize == 3 ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, ufoSupportNPOT() ? texData:data);
		
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
		glBindTexture(GL_TEXTURE_2D, 0);
		if (data)
            free(data);
		hTex->id = texId;
	}
#else
	if (g_pd3dDevice) {
		LPDIRECT3DTEXTURE9 pTex = 0;
		D3DLOCKED_RECT lockRect = {0};
		RECT rcLock = {0, 0, width, height};

		IDirect3DDevice9_CreateTexture(g_pd3dDevice, hTex->width_POT, hTex->height_POT, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pTex, 0);

		hTex->id = (void*)pTex;
		IDirect3DTexture9_LockRect(pTex, 0, &lockRect, &rcLock, 0);

		{
			int x = 0, y = 0;
			unsigned char * bit = (unsigned char*)lockRect.pBits;
			for (y = 0; y < height; ++y) {
				unsigned char* _bit = bit;	
				for (x = 0; x < width; ++x) {
					_bit[0] = texData[2];
					_bit[1] = texData[1];
					_bit[2] = texData[0];
					_bit[3] = pixelSize == 3 ? 255 : texData[3];
					_bit += 4;
					texData += pixelSize;
				}
				bit += lockRect.Pitch;
			}
		}
		IDirect3DTexture9_UnlockRect(pTex, 0);
	}
#endif

	return hTex;
}

int ufoTextureWidth(HTEXTURE texture)
{
	return texture ? texture->width : 0;
}

int ufoTextureHeight(HTEXTURE texture)
{
	return texture ? texture->height : 0;
}

int ufoDeleteTexture(int texture)
{
#ifdef OGL_RENDER

#else

#endif
	return 0;
}

int ufoSetTextureFilter(HTEXTURE texture, UFO_TEXTURE_FILTER filter, UFO_TEXTURE_WRAP wrap)
{
	if (!texture) {
		assert(0);
		return 0;
	}

	texture->filter = filter;
	texture->wrap = wrap;

#ifdef OGL_RENDER//### ogl can pre deal
	glBindTexture(GL_TEXTURE_2D, (GLuint)texture->id);
	switch (filter) {
	case UFO_TEXTURE_FILTER_LINE:
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			break;
		}
	case UFO_TEXTURE_FILTER_POINT:
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

			break;
		}
	}

	switch (wrap) {
	case UFO_TEXTURE_WRAP_REPEAT:
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			break;
		}

	case UFO_TEXTURE_WRAP_CLAMP:
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F);
			break;
		}
	}
#else
	//g_d3d9TexFilter[texture] = filter;
	//g_d3d9TexWrap[texture] = wrap;
#endif
	return 1;
}

int ufoRenderPoint(int nCount, float *Vertex, ufoColor color, float dpi)
{
#ifdef OGL_RENDER
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glColor4f(color.red, color.green, color.blue,  color.alpha);
	glVertexPointer(3, GL_FLOAT, 0, Vertex);
	glDrawArrays(GL_POINTS, 0, nCount);
#else
	int i = 0;
	CUSTOMVERTEX* pVertices = 0;

	IDirect3DVertexBuffer9_Lock(g_pVB, 0, 0, (void**)&pVertices, 0);
	for (i = 0; i < nCount; ++i) {

		pVertices[i].position.x =  Vertex[i*3+0];
		pVertices[i].position.y =  -Vertex[i*3+1];
		pVertices[i].position.z =  Vertex[i*3+2];
	}

	IDirect3DVertexBuffer9_Unlock(g_pVB);
	IDirect3DDevice9_SetTexture(g_pd3dDevice, 0, 0);
	IDirect3DDevice9_DrawPrimitive(g_pd3dDevice, D3DPT_POINTLIST, 0, nCount);
#endif
	return 1;
}

int ufoRenderTexture(int nTexs, HTEXTURE *texture, int nCount, float *Vertex, 
	float *texCoord, const ufoColor *color, float alpha, float dpi)
{	
	if (dpi == 0.0f) {
		dpi = ufoGetDPI();
	}
	
	if ((!nTexs || !texture || !texCoord) && !color) {
		alpha = 0.0f;
	}

#ifdef OGL_RENDER
	{
		int i, k;
        int bStencil = 0;
		for (i = 0; i < nCount; i += 4) {
			ufoMatrix4 matrix = {0};
			float e03, e13;
			ufoRenderGetMatrix(UFO_MODELVIEW, &matrix);
			e03 = matrix.e03;
			e13 = matrix.e13;
			matrix.e03 = (int)(matrix.e03-0.5f);
			matrix.e13 = (int)(matrix.e13);
			ufoRenderLoadMatrix(UFO_MODELVIEW, &matrix);

			if (color) {
				glColor4f(alpha*color->alpha*color->red, alpha*color->alpha*color->green, alpha*color->alpha*color->blue, alpha*color->alpha);
			}
			else {
				glColor4f(alpha, alpha, alpha, alpha);
			}

			for (k = 0; k < 4; ++k) {
				(*(Vertex+i*3+k*3+0)) =  ((*(Vertex+i*3+k*3+0))*dpi);
				(*(Vertex+i*3+k*3+1)) =  ((*(Vertex+i*3+k*3+1))*dpi);

				(*(Vertex+i*3+k*3+0)) = (int)(*(Vertex+i*3+k*3+0));
				(*(Vertex+i*3+k*3+1)) = (int)(*(Vertex+i*3+k*3+1));
			}

			if (texCoord && texture && nTexs) {
				if (nTexs == 1 && texture[0]) {
					glBindTexture(GL_TEXTURE_2D, (int)texture[0]->id);
					glTexCoordPointer(2, GL_FLOAT, 0, texCoord+i*2);
				}
				else if (nTexs > 1){
					for (k = nTexs-1; k >= 0; --k) {
						glActiveTexture(GL_TEXTURE0+k);
						glEnable(GL_TEXTURE_2D);
						
						if (texture[k])
							glBindTexture(GL_TEXTURE_2D, (int)texture[k]->id);
						else
							glBindTexture(GL_TEXTURE_2D, 0);

						glClientActiveTexture(GL_TEXTURE0+k);
						glEnableClientState(GL_TEXTURE_COORD_ARRAY);
						glTexCoordPointer(2, GL_FLOAT, 0, texCoord+i*2);
					}
                    bStencil = 1;
				}
			}
			else {
                glBindTexture(GL_TEXTURE_2D, 0);
				glDisable(GL_TEXTURE_2D);
                bStencil = 1;
			}
            
			glVertexPointer(3, GL_FLOAT, 0, Vertex+i*3);
			glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

			if (nTexs > 1) {
				for (k = 1; k < nTexs; ++k) {
					glActiveTexture(GL_TEXTURE0+k);
					glDisable(GL_TEXTURE_2D);

					glClientActiveTexture(GL_TEXTURE0+k);
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				}
			}
            
            if (bStencil) {
                glActiveTexture(GL_TEXTURE0);
                glClientActiveTexture(GL_TEXTURE0);
                glEnable(GL_TEXTURE_2D);
            }

			matrix.e03 = e03;
			matrix.e13 = e13;
			ufoRenderLoadMatrix(UFO_MODELVIEW, &matrix);
		}
	}
#else
	if (g_pIB) {
		CUSTOMVERTEX* pVertices = 0;
		int i = 0;
		WORD *pIndices = 0;

		IDirect3DIndexBuffer9_Lock(g_pIB, 0, 0, &pIndices, 0);

		for (i = 0; i < nCount; i += 4) {
			pIndices[0+i/4*6] = 2+i;
			pIndices[1+i/4*6] = 1+i;
			pIndices[2+i/4*6] = 0+i;

			pIndices[3+i/4*6] = 2+i;
			pIndices[4+i/4*6] = 3+i;
			pIndices[5+i/4*6] = 1+i;
		}

		IDirect3DIndexBuffer9_Unlock(g_pIB);

		if (nTexs == 0) {
			IDirect3DDevice9_SetTexture(g_pd3dDevice, 0, 0);
		}
		else {
			if (nTexs == 1) {
				IDirect3DDevice9_SetTexture(g_pd3dDevice, 0, (LPDIRECT3DBASETEXTURE9)(texture[0]->id));
				if (texture[0]->filter == UFO_TEXTURE_FILTER_POINT) {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
				}
				else {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
				}

				if (texture[0]->wrap == UFO_TEXTURE_WRAP_REPEAT) {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
				}
				else {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
				}
			}
			else {
				IDirect3DDevice9_SetTexture(g_pd3dDevice, 0, (LPDIRECT3DBASETEXTURE9)texture[0]->id);
				if (texture[0]->filter == UFO_TEXTURE_FILTER_POINT) {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
				}
				else {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
				}

				if (texture[0]->wrap == UFO_TEXTURE_WRAP_REPEAT) {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
				}
				else {
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
					IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
				}

				IDirect3DDevice9_SetTexture(g_pd3dDevice, 1, (LPDIRECT3DBASETEXTURE9)texture[1]->id);
				
				IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 1, D3DTSS_COLOROP, D3DTOP_MODULATE);
				IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,	1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
				IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,	1, D3DTSS_TEXCOORDINDEX, 0);
				
				IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
				IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

				IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
				IDirect3DDevice9_SetSamplerState(g_pd3dDevice, 1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

				IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 1,D3DTSS_ALPHAOP, D3DTOP_MODULATE);
				IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,	1,D3DTSS_ALPHAARG1, D3DTA_TEXTURE );

			}
		}

		IDirect3DVertexBuffer9_Lock(g_pVB, 0, 0, (void**)&pVertices, 0);
		for (i = 0; i < nCount; ++i) {
			unsigned int _alpha = (unsigned int)(alpha*255);

			pVertices[i].position.x =  Vertex[i*3+0]*dpi;
			pVertices[i].position.y = -Vertex[i*3+1]*dpi;
			pVertices[i].position.z =  Vertex[i*3+2];

			if (nTexs != 0) {
				pVertices[i].tu = texCoord ? texCoord[i*2+0] : 0;
				pVertices[i].tv = texCoord ? texCoord[i*2+1] : 0;
			}
			else {
				pVertices[i].tu = 0;
				pVertices[i].tv = 0;
			}

			if (color) {
				unsigned int __alpha = _alpha*color->alpha;
				unsigned int _red = __alpha*color->red;
				unsigned int _green = __alpha*color->green;
				unsigned int _blue = __alpha*color->blue;
				pVertices[i].color = (_red << 24) | (_green << 16) | (_blue << 8) | (__alpha << 0);
			}
			else {
				pVertices[i].color = (_alpha << 24) | (_alpha << 16) | (_alpha << 8) | (_alpha << 0);
			}
		}

		IDirect3DVertexBuffer9_Unlock(g_pVB);
		IDirect3DDevice9_DrawIndexedPrimitive(g_pd3dDevice, D3DPT_TRIANGLELIST, 0, 0, nCount, 0, nCount/2);

		if (nTexs > 1) {
			IDirect3DDevice9_SetTexture(g_pd3dDevice, 1, 0);
			IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
			IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 1,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		}
	}
#endif
	return 1;
}

int ufoRenderCanvas(HCANVAS canvas, const ufoRect *rectDst, const ufoColor *color, float alpha, float dpi)
{
	float textureWidth = canvas->texture->width_POT;
	float textureHeight = canvas->texture->height_POT;

	float x = rectDst->left;
	float y = -rectDst->top;
	float w = rectDst->right-rectDst->left;
	float h = rectDst->bottom-rectDst->top;

	float dx = 0;
	float dy = 0;
	float dw = canvas->texture->width;
	float dh = canvas->texture->height;

	float texCoordBuffer[] = {
		(float)dx/(float)textureWidth,		(float)(dh)/(float)textureHeight,
		(float)(dx+dw)/(float)textureWidth,	(float)(dh)/(float)textureHeight,
		(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
		(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
	};

	float vertexBuffer[] = {
		(float)x,		(float)y-h,	(float)0,
		(float)x+w,	(float)y-h,	(float)0,
		(float)x,		(float)y,		(float)0,
		(float)x+w,	(float)y,		(float)0
	};

	ufoRenderTexture(1, &canvas->texture, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);
	return 1;
}

int ufoBlendCanvas(HCANVAS canvas, HTEXTURE textures, const ufoRect *rectDst, const ufoColor *color, float alpha, float dpi)
{
	HTEXTURE src[2] = {canvas->texture, textures};
	float textureWidth = canvas->texture->width_POT;
	float textureHeight = canvas->texture->height_POT;

	float x = rectDst->left;
	float y = -rectDst->top;
	float w = rectDst->right-rectDst->left;
	float h = rectDst->bottom-rectDst->top;

	float dx = 0;
	float dy = 0;
	float dw = canvas->texture->width;
	float dh = canvas->texture->height;

	float texCoordBuffer[] = {
		(float)dx/(float)textureWidth,		(float)(dh)/(float)textureHeight,
		(float)(dx+dw)/(float)textureWidth,	(float)(dh)/(float)textureHeight,
		(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
		(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
	};

	float vertexBuffer[] = {
		(float)x,		(float)y-h,	(float)0,
		(float)x+w,	(float)y-h,	(float)0,
		(float)x,		(float)y,		(float)0,
		(float)x+w,	(float)y,		(float)0
	};

	ufoRenderTexture(2, src, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);

	return 1;
}

void tettt(const char* s)
{
	int a = 0;
    int b = 0;
    a = b;
}

int ufoRestore(void* wParam, void* lParam)
{
	g_screenRect = *(ufoRect*)wParam;

#ifdef OGL_RENDER
	
#if defined(_WIN32)
	if (!g_hDC && !g_hRC) {
		PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
		{
			sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
			1,											// Version Number
			PFD_DRAW_TO_WINDOW |						// Format Must Support Window
			PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		//	PFD_DIRECT3D_ACCELERATED |
			//PFD_SWAP_LAYER_BUFFERS |
			PFD_DOUBLEBUFFER,							// Must Support Double Buffering
			PFD_TYPE_RGBA,								// Request An RGBA Format
			32,										// Select Our Color Depth
			0, 0, 0, 0, 0, 0,							// Color Bits Ignored
			0,											// No Alpha Buffer
			0,											// Shift Bit Ignored
			0,											// No Accumulation Buffer
			0, 0, 0, 0,									// Accumulation Bits Ignored
			24,											// Z-Buffer (Depth Buffer)  
			8,											// Stencil Buffer
			0,											// No Auxiliary Buffer
			PFD_MAIN_PLANE,								// Main Drawing Layer
			0,											// Reserved
			0, 0, 0										// Layer Masks Ignored
		};
		GLuint PixelFormat;			// Holds The Results After Searching For A Match
		GLboolean ret = 0;
		g_hDC = GetDC((HWND)lParam);
		PixelFormat = ChoosePixelFormat(g_hDC, &pfd);
		SetPixelFormat(g_hDC, PixelFormat, &pfd);
		g_hRC = wglCreateContext(g_hDC);
		wglMakeCurrent(g_hDC, g_hRC);

		glewInit();//### init glew for windows's opengl
		ret = glewIsSupported("GL_VERSION_1_3");
		ret = 0;
	}
#endif
	glFrontFace(GL_CCW);
	//glEnable(GL_CULL_FACE);

	glDisable(GL_DITHER);
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_FOG);
    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_STENCIL_TEST);

    glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);

	glClientActiveTexture(GL_TEXTURE0);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glEnableClientState(GL_VERTEX_ARRAY);

	//glDepthMask(GL_FALSE);
	glDepthFunc(GL_ALWAYS);//### mac osx need it!

	{
		char* s = (char*)glGetString(GL_EXTENSIONS);
		int a = 0;
		tettt(s);
		ufoLog("getstring", "string=%s", s);
	}
	

#else
	{
		HRESULT hr = 0;
		D3DPRESENT_PARAMETERS d3dpp = {0};
		d3dpp.Windowed = TRUE;
		d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;//### because we need back buffer for dirty, so no multisample
		d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
		d3dpp.BackBufferCount = 1;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;//ufoGetAppInfo()->winStyle == LAYERED_WIN ? 0: D3DMULTISAMPLE_2_SAMPLES;//### only for 2x fass, because tencent's computer is slow
		d3dpp.EnableAutoDepthStencil = TRUE;
		d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;

		if (g_pVB) {
			IDirect3DVertexBuffer9_Release(g_pVB);
			g_pVB = NULL;
		}

		if (g_pIB) { 
			IDirect3DIndexBuffer9_Release(g_pIB);
			g_pIB = NULL;
		}

		if (g_backbuffer) {
			IDirect3DSurface9_Release(g_backbuffer);
			g_backbuffer = 0;
		}

		if (!g_pD3D) {
			g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
			IDirect3D9_CreateDevice(g_pD3D,D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,(HWND)lParam,
				D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
				&d3dpp,&g_pd3dDevice );
		}
		else
			hr = IDirect3DDevice9_Reset(g_pd3dDevice, &d3dpp);

		if (!g_pVB) {
			IDirect3DDevice9_CreateVertexBuffer(g_pd3dDevice, 1000*4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL);
		}		

		if (!g_pIB) {
			IDirect3DDevice9_CreateIndexBuffer(g_pd3dDevice, 9*6*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, 0);
		}

		if (!g_systemBuffer) {
			ufoRect rc = {0};
			ufoGetScreenRect(&rc);
			//IDirect3DDevice9_CreateTexture(g_pd3dDevice, width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &g_systemTex, 0);
			//IDirect3DTexture9_GetSurfaceLevel(g_systemTex, 0, &g_systemBuffer);
			IDirect3DDevice9_CreateOffscreenPlainSurface(g_pd3dDevice, (UINT)((rc.right-rc.left)*ufoGetDPI()), 
				(UINT)((rc.bottom-rc.top)*ufoGetDPI()), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &g_systemBuffer, 0);
		}

		IDirect3DDevice9_GetRenderTarget(g_pd3dDevice, 0, &g_backbuffer);
		IDirect3DDevice9_GetDepthStencilSurface(g_pd3dDevice, &g_backDepthBuffer);

		IDirect3DDevice9_SetIndices(g_pd3dDevice, g_pIB);
		IDirect3DDevice9_SetStreamSource(g_pd3dDevice, 0, g_pVB, 0, sizeof(CUSTOMVERTEX));
		IDirect3DDevice9_SetFVF(g_pd3dDevice, D3DFVF_CUSTOMVERTEX);

		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_LIGHTING, FALSE );      

		//IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_BLENDFACTOR, 0xffffffff);

		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 0,D3DTSS_COLOROP, D3DTOP_MODULATE);
		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,0,D3DTSS_COLORARG1, D3DTA_TEXTURE );
		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,0,D3DTSS_COLORARG2, D3DTA_DIFFUSE );

		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice, 0,D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,0,D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
		IDirect3DDevice9_SetTextureStageState(g_pd3dDevice,0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

		IDirect3DDevice9_SetRenderState(g_pd3dDevice, D3DRS_ZWRITEENABLE, FALSE);
	}
#endif

	ufoSetAlphaBlend(1);
	ufoSetDepthTest(0);
    ufoSetCullFace(1);
    
	if (!g_stack_projection) {
		ufoMatrix4 matrix = {0};
		g_stack_projection = ufoStackCreate(sizeof(ufoMatrix4));
		ufoIndentityMatrix(&matrix);
		ufoStackPush(g_stack_projection, &matrix);
	}

	if (!g_stack_modelview) {
		ufoMatrix4 matrix = {0};
		g_stack_modelview = ufoStackCreate(sizeof(ufoMatrix4));
		ufoIndentityMatrix(&matrix);
		ufoStackPush(g_stack_modelview, &matrix);
	}

	if (!g_stack_viewport) {
		ufoRect rect = {0};
		g_stack_viewport = ufoStackCreate(sizeof(ufoRect));
		ufoStackPush(g_stack_viewport, &rect);
	}

	if (g_defaultCanvas) {
		free(g_defaultCanvas->texture);
		free(g_defaultCanvas);
		g_defaultCanvas = 0;
	}

	if (!g_defaultCanvas) {
		ufoRect rcScreen = {0};
		
		g_defaultCanvas = (HCANVAS)malloc(sizeof(tagCANVAS));
		memset(g_defaultCanvas, 0, sizeof(tagCANVAS));

		ufoGetScreenRect(&rcScreen);
		g_defaultCanvas->texture = (HTEXTURE)malloc(sizeof(tagTEXTURE));
		memset(g_defaultCanvas->texture, 0, sizeof(tagTEXTURE));

		g_defaultCanvas->texture->width = (rcScreen.right-rcScreen.left)*ufoGetDPI();
		g_defaultCanvas->texture->height = (rcScreen.bottom-rcScreen.top)*ufoGetDPI();

#ifdef OGL_RENDER

#ifdef OGL_ES
		glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint*)&g_defaultCanvas->depthbuffer);
		glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, (GLint*)&g_defaultCanvas->framebuffer);
#else
		glGetIntegerv(GL_RENDERBUFFER_BINDING, (GLint*)&g_defaultCanvas->depthbuffer);
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&g_defaultCanvas->framebuffer);
#endif
		ufoLog("GL_RENDERBUFFER_BINDING", "GL_RENDERBUFFER_BINDING = %d GL_FRAMEBUFFER_BINDING = %d", 
			g_defaultCanvas->depthbuffer, g_defaultCanvas->framebuffer);
#endif
	}

	g_currentCanvas = g_defaultCanvas;

	return 1;
}

int ufoPresent2D(float width, float height)
{
#ifdef OGL_RENDER	
	ufoRenderOrthoMatrix(UFO_PROJECTION, 0.0f, width, 0.0f, height, -10000, 10000);
	//ufoRenderTranslate(UFO_PROJECTION, 0.0f, -(float)height, 0.0f);
	if (ufoCanvasGetCurrent() == g_defaultCanvas) {//### default canvas, we need flip aisx Y, openGL bug
		ufoRenderTranslate(UFO_PROJECTION, 0.0f, -(float)height, 0.0f);
	}
	else
		ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);
#else

	ufoRenderOrthoMatrix(UFO_PROJECTION, 0.0f, width, -height, 0.0f, -1.0, 1.0);
	ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);
	ufoRenderTranslate(UFO_PROJECTION, -0.5f/ufoGetDPI(), -0.5f/ufoGetDPI(), 0.0f/ufoGetDPI());

#endif

	g_render = UFO_RENDER_2D;

	return 1;
}

int ufoPresent3D(float _width, float _height, float fovy)
{
	if (fovy == 0.0f)
		fovy = 3.1415926f / 8.0f;
	else
		fovy = fovy / 57.2957795130823782f;	

	{
		float width = (float)_width*ufoGetDPI();
		float height = (float)_height*ufoGetDPI();
		float aspect = width/height;
		float oneDivCtan = 1/(float)tan(0.5f * fovy);
		float desiredZ = -(width * oneDivCtan) / (2 * aspect);
		float nearZ = (-desiredZ) * 0.5f;
		float farZ = (-desiredZ) * 2.0f;

#ifdef OGL_RENDER
		{	
			ufoMatrix4 perspective, lookat;
			ufoVector3 eye = {0, 0, -desiredZ};
			ufoVector3 center = {0, 0, 0};
			ufoVector3 up = {0, 1, 0};

			ufoPerspectiveMatrix(&perspective, fovy, aspect, nearZ, farZ);
			ufoRenderLoadMatrix(UFO_PROJECTION, &perspective);
			
			if (ufoCanvasGetCurrent() != g_defaultCanvas)//### only opengl need flip Y 
				ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);

			ufoMatrixLookAt(&lookat, &eye, &center, &up);
			ufoRenderLoadMatrix(UFO_MODELVIEW, &lookat);

			ufoRenderTranslate(UFO_MODELVIEW, -_width/2, -_height/2, 0);
		}
#else
		{
			ufoMatrix4 perspective, lookat;
			ufoVector3 eye = {0, 0, -desiredZ};
			ufoVector3 center = {0, 0, 0};
			ufoVector3 up = {0, 1, 0};

			ufoPerspectiveMatrix(&perspective, fovy, aspect, nearZ, farZ);
			ufoRenderLoadMatrix(UFO_PROJECTION, &perspective);

			ufoMatrixLookAt(&lookat, &eye, &center, &up);
			ufoRenderLoadMatrix(UFO_MODELVIEW, &lookat);

			ufoRenderScale(UFO_MODELVIEW, 1.0f, -1.0f, 1.0f);
			ufoRenderTranslate(UFO_PROJECTION, -0.5f/ufoGetDPI(), +0.5f/ufoGetDPI(), 0.0f/ufoGetDPI());

			ufoRenderTranslate(UFO_MODELVIEW, -_width/2, -_height/2, 0);
		}
#endif
	}
	g_render = UFO_RENDER_3D;

	return 1;
}

int ufoRenderLoadProjection(UFO_RENDER projection, float _width, float _height, float fovy)
{
	if (fovy == 0.0f)
		fovy = 3.1415926f / 8.0f;
	else
		fovy = fovy / 57.2957795130823782f;	

	if (projection == UFO_RENDER_3D) {
		float width = (float)_width*ufoGetDPI();
		float height = (float)_height*ufoGetDPI();
		float aspect = width/height;
		float oneDivCtan = 1/(float)tan(0.5f * fovy);
		float desiredZ = -(width * oneDivCtan) / (2 * aspect);
		float nearZ = (-desiredZ) * 0.5f;
		float farZ = (-desiredZ) * 2.0f;

#ifdef OGL_RENDER
		{	
			ufoMatrix4 perspective;
			ufoPerspectiveMatrix(&perspective, fovy, aspect, nearZ, farZ);
			ufoRenderLoadMatrix(UFO_PROJECTION, &perspective);

			if (ufoCanvasGetCurrent() != g_defaultCanvas)//### only opengl need flip Y
				ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);
		}
#else
		{	
			ufoMatrix4 perspective;
			ufoPerspectiveMatrix(&perspective, fovy, aspect, nearZ, farZ);
			ufoRenderLoadMatrix(UFO_PROJECTION, &perspective);
			ufoRenderTranslate(UFO_PROJECTION, -0.5f/ufoGetDPI(), +0.5f/ufoGetDPI(), 0.0f/ufoGetDPI());
		}
#endif
	}
	else {
#ifdef OGL_RENDER	
		ufoRenderOrthoMatrix(UFO_PROJECTION, 0.0f, _width, 0.0f, _height, -1, 1);
		//ufoRenderTranslate(UFO_PROJECTION, 0.0f, -(float)height, 0.0f);
		if (ufoCanvasGetCurrent() == g_defaultCanvas) {//### default canvas, we need flip aisx Y, openGL bug
			ufoRenderTranslate(UFO_PROJECTION, 0.0f, -(float)_height, 0.0f);
		}
		else
			ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);
#else
		ufoRenderOrthoMatrix(UFO_PROJECTION, 0.0f, _width, -_height, 0.0f, -1.0, 1.0);
		ufoRenderScale(UFO_PROJECTION, 1.0f, -1.0f, 1.0f);
		ufoRenderTranslate(UFO_PROJECTION, -0.5f/ufoGetDPI(), -0.5f/ufoGetDPI(), 0.0f/ufoGetDPI());

#endif
	}
	return 1;
}

UFO_RENDER ufoGetPresent(float *width, float *height)
{
	return g_render;
}

HCANVAS ufoCreateCanvas(float width, float height)
{
	HCANVAS hCanvas = (HCANVAS)malloc(sizeof(tagCANVAS));
	int width_pot = ufoSupportNPOT() ? width*ufoGetDPI() : internal_render_calc_pot(width*ufoGetDPI());
	int height_pot = ufoSupportNPOT() ? height*ufoGetDPI() : internal_render_calc_pot(height*ufoGetDPI());
	memset(hCanvas, 0, sizeof(tagCANVAS));
	
#ifdef OGL_RENDER

	hCanvas->texture = ufoGenTextures(0, width*ufoGetDPI(), height*ufoGetDPI(), 4);

#ifdef OGL_ES
	ufoLog("hCanvas->texture", "canvas width = %d, canvas height = %d", hCanvas->texture->width_POT, hCanvas->texture->height_POT);
	
	glGenFramebuffersOES(1, &hCanvas->framebuffer);

	ufoLog("glGenFramebuffersOES", "glGenFramebuffersOES =%d", hCanvas->framebuffer);

	glBindFramebufferOES(GL_FRAMEBUFFER_OES, hCanvas->framebuffer);
	glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, hCanvas->texture->id, 0);
	
#ifndef ANDROID
	glGenRenderbuffersOES(1, &hCanvas->depthbuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, hCanvas->depthbuffer);

	ufoLog("glGenRenderbuffersOES", "glGenRenderbuffersOES =%d", hCanvas->depthbuffer);

	glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH24_STENCIL8_OES, width_pot, height_pot);

	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, hCanvas->depthbuffer);
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, hCanvas->depthbuffer);
#endif
	if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
		ufoLog("create framebuffer fail!", "create framebuffer fail!");
		assert(0);
	}
#else

	glGenFramebuffers(1, &hCanvas->framebuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, hCanvas->framebuffer);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hCanvas->texture->id, 0);

	glGenRenderbuffers(1, &hCanvas->depthbuffer);
	glBindRenderbuffer(GL_RENDERBUFFER, hCanvas->depthbuffer);

	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width_pot, height_pot);

	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hCanvas->depthbuffer);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, hCanvas->depthbuffer);

	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		assert(0);

#endif

#else
	{
		LPDIRECT3DTEXTURE9 pTex = 0;
		LPDIRECT3DSURFACE9 pSurface = 0;
		D3DLOCKED_RECT lockRect = {0};
		RECT rcLock = {0, 0, (LONG)width_pot, (LONG)height_pot};
		IDirect3DDevice9_CreateTexture(g_pd3dDevice, (UINT)width_pot, (UINT)height_pot, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pTex, 0);
		
		hCanvas->texture = (HTEXTURE)malloc(sizeof(tagTEXTURE));
		memset(hCanvas->texture, 0, sizeof(tagTEXTURE));
		hCanvas->texture->id = pTex;
		hCanvas->texture->width = width*ufoGetDPI();
		hCanvas->texture->height = height*ufoGetDPI();
		hCanvas->texture->width_POT = width_pot;
		hCanvas->texture->height_POT = height_pot;

		IDirect3DDevice9_CreateDepthStencilSurface(g_pd3dDevice, (UINT)width_pot, (UINT)height_pot, 
			D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &pSurface, NULL);

		hCanvas->depthbuffer = pSurface;
	}
#endif

	return hCanvas;
}

HCANVAS ufoCanvasGetCurrent()
{
	return g_currentCanvas;
}

HCANVAS ufoCanvasGetDefault()
{
	return g_defaultCanvas;
}

int ufoSetCanvas(HCANVAS canvas)
{
	if (canvas == 0) {
		canvas = g_currentCanvas = g_defaultCanvas;
	}

#ifdef OGL_RENDER
	if (canvas == g_defaultCanvas) {
		glFrontFace(GL_CCW);//### opengl use framebuffer will upside to down, so need re cullface
	#ifdef OGL_ES
		#if TARGET_OS_IPHONE == 1
			glBindFramebufferOES(GL_FRAMEBUFFER_OES, 1);//### 2 for mutil-sample
            glBindRenderbufferOES(GL_RENDERBUFFER_OES, canvas->depthbuffer);
		#else
			glBindFramebufferOES(GL_FRAMEBUFFER_OES, canvas->framebuffer);
			glBindRenderbufferOES(GL_RENDERBUFFER_OES, canvas->depthbuffer);
		#endif
	#else
		glBindFramebuffer(GL_FRAMEBUFFER, canvas->framebuffer);
	#endif
		
	}
	else{
		glFrontFace(GL_CW);//### opengl use framebuffer will upside to down, so need re cullface
	#ifdef OGL_ES
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, canvas->framebuffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, canvas->depthbuffer);
	#else
		glBindFramebuffer(GL_FRAMEBUFFER, canvas->framebuffer);
	#endif
	}

#else
	{
		ufoRect rcViewport = {0};
		ufoGetViewPort(&rcViewport);//### directX use renderTarget will reSet viewport 
		if (canvas == g_defaultCanvas) {//### render to screen
			IDirect3DDevice9_SetRenderTarget(g_pd3dDevice, 0, g_backbuffer);
			IDirect3DDevice9_SetDepthStencilSurface(g_pd3dDevice, g_backDepthBuffer);
		}
		else {
			LPDIRECT3DSURFACE9 pSurface = 0;
			LPDIRECT3DTEXTURE9 pTarget = canvas->texture->id;
			IDirect3DTexture9_GetSurfaceLevel(pTarget, 0, &pSurface);
			if (pSurface) {
				IDirect3DDevice9_SetRenderTarget(g_pd3dDevice, 0, pSurface);
				IDirect3DSurface9_Release(pSurface);
			}
			IDirect3DDevice9_SetDepthStencilSurface(g_pd3dDevice, canvas->depthbuffer);
		}
		ufoSetViewPort(&rcViewport);//### directX use renderTarget will reSet viewport 
	}

#endif
	g_currentCanvas = canvas;
	return 1;
}

int ufoRenderPushMatrix(UFO_MATRIXMODE mode)
{
	UFO_Stack stack = 0;
	ufoMatrix4 *topMatrix = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	topMatrix = (ufoMatrix4*)ufoStackTop(stack);
	ufoStackPush(stack, topMatrix);

	return 1;
}

int ufoRenderPopMatrix(UFO_MATRIXMODE mode)
{
	UFO_Stack stack = 0;
	ufoMatrix4 *topMatrix = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	if (ufoStackGetSize(stack) < 2) {
		assert(0);
		return 0;
	}

	ufoStackPop(stack);
	topMatrix = (ufoMatrix4*)ufoStackTop(stack);
	ufoRenderLoadMatrix(mode, topMatrix);
	return 1;
}

int ufoRenderGetMatrix(UFO_MATRIXMODE mode, ufoMatrix4 *matrix)
{
	UFO_Stack stack = 0;
	ufoMatrix4 *topMatrix = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	if (ufoStackGetSize(stack) < 1 || !matrix) {
		assert(0);
		return 0;
	}

	topMatrix = (ufoMatrix4*)ufoStackTop(stack);
	*matrix = *topMatrix;

	return 1;
}

int ufoGetMartixCount(UFO_MATRIXMODE mode)
{
	UFO_Stack stack = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	return ufoStackGetSize(stack);
}

/*int ufoGetMartixByIndex(UFO_MATRIXMODE mode, int index, ufoMatrix4 *matrix)
{
	UFO_Stack stack = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;
	return 0;
}*/

int ufoRenderIdentityMatrix(UFO_MATRIXMODE mode)
{
	ufoMatrix4 matrix;
	ufoIndentityMatrix(&matrix);
	ufoRenderLoadMatrix(mode, &matrix);

	return 1;
}

int ufoRenderLoadMatrix(UFO_MATRIXMODE mode, ufoMatrix4 *matrix)
{
	UFO_Stack stack = 0;
	ufoMatrix4 *topMatrix = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	topMatrix = (ufoMatrix4*)ufoStackTop(stack);
	*topMatrix = *matrix;

#ifdef OGL_RENDER
	if (mode == UFO_PROJECTION) 
		glMatrixMode(GL_PROJECTION);
	else
		glMatrixMode(GL_MODELVIEW);

	glLoadMatrixf(topMatrix->mElements);
#else
	if (mode == UFO_PROJECTION) 
		IDirect3DDevice9_SetTransform(g_pd3dDevice, D3DTS_PROJECTION, (const D3DMATRIX *)topMatrix->mElements);
	else
		IDirect3DDevice9_SetTransform(g_pd3dDevice, D3DTS_VIEW, (const D3DMATRIX *)topMatrix->mElements);
#endif
	return 1;
}

int ufoRenderMultMatrix(UFO_MATRIXMODE mode, ufoMatrix4 *matrix)
{
	UFO_Stack stack = 0;
	ufoMatrix4 *topMatrix = 0;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	topMatrix = (ufoMatrix4*)ufoStackTop(stack);

	ufoMultMatrix(topMatrix, topMatrix, matrix);

	ufoRenderLoadMatrix(mode, topMatrix);
	return 1;
}

int ufoRenderTranslate(UFO_MATRIXMODE mode, float x, float y, float z)
{	
	ufoMatrix4 matrix;

#ifdef OGL_RENDER
	ufoTranslateMatrix(&matrix, x*ufoGetDPI(), -y*ufoGetDPI(), z*ufoGetDPI());
#else
	ufoTranslateMatrix(&matrix, x*ufoGetDPI(), y*ufoGetDPI(), z*ufoGetDPI());
#endif
	ufoRenderMultMatrix(mode, &matrix);
	return 1;
}

int ufoRenderScale(UFO_MATRIXMODE mode, float x, float y, float z)
{
	ufoMatrix4 matrix;
	ufoScaleMatrix(&matrix, x, y, z);
	ufoRenderMultMatrix(mode, &matrix);
	return 1;
}

int ufoRenderRotate(UFO_MATRIXMODE mode, float fradian, float x, float y, float z)
{
	UFO_Stack stack = 0;
	ufoMatrix4 matrix;
	if (mode == UFO_PROJECTION)
		stack = g_stack_projection;
	else
		stack = g_stack_modelview;

	ufoMatrixRotate(&matrix, fradian, x, y, z);
	ufoRenderMultMatrix(mode, &matrix);

	return 1;
}

int ufoRenderOrthoMatrix(UFO_MATRIXMODE mode, float left, float right, float bottom, float top, float nearVal, float farVal)
{
	ufoMatrix4 matrix;
	ufoOrthoMatrix(&matrix, left*ufoGetDPI(), right*ufoGetDPI(), bottom*ufoGetDPI(), 
		top*ufoGetDPI(), nearVal, farVal);
	ufoRenderLoadMatrix(mode, &matrix);	
	return 1;
}

int ufoSupportNPOT()
{
	return 1;
}

void ufoTest()
{
    /*glMatrixMode(GL_TEXTURE);
    //glLoadIdentity();
    glScalef(1.0f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);*/
}